package jocode;

import javax.media.opengl.*;

/**
 * Provide access to the ARBMultitexture extension.  To use:
 * <PRE>
 * 		setup() {
 * 			JOMultiTexture.setup();
 *     		texture1 = makeTexture("images/wood.jpg");
 *     		texture2 = makeTexture("images/splatter.jpg");
 		}
 * 
 * 		render() {
 * 			JOMultiTexture.activateTexture(0,texture1);
 * 			JOMultiTexture.activateTexture(1,texture2);
 * 			JOMultiTexture.drawQuadMT(0,0,0,5,5); 
 * 		}
 * </PRE>
 * 
 * If you want to switch to non multi-texturing drawing, you need to select GL_TEXTURE0
 * as the last texture unit:<BR>
 *			gl.glActiveTexture(GL_TEXTURE0);
 *
 */
public class JOMultiTexture extends JOApp {
	private static int maxTextureUnits = 0;
	private static int[] textures = new int[4];
	private static int supported = -1;
	
	private static int[] ARB_TEXTURE_NUMBERS = new int[] {
		GL.GL_TEXTURE0,
		GL.GL_TEXTURE1,
		GL.GL_TEXTURE2,
		GL.GL_TEXTURE3,
		GL.GL_TEXTURE4,
		GL.GL_TEXTURE5,
		GL.GL_TEXTURE6,
		GL.GL_TEXTURE7,
		GL.GL_TEXTURE8,
		GL.GL_TEXTURE9,
		GL.GL_TEXTURE10,
		GL.GL_TEXTURE11,
		GL.GL_TEXTURE12,
		GL.GL_TEXTURE13,
		GL.GL_TEXTURE14,
		GL.GL_TEXTURE15,
		GL.GL_TEXTURE16,
		GL.GL_TEXTURE17,
		GL.GL_TEXTURE18,
		GL.GL_TEXTURE19,
		GL.GL_TEXTURE20,
		GL.GL_TEXTURE21,
		GL.GL_TEXTURE22,
		GL.GL_TEXTURE23,
		GL.GL_TEXTURE24,
		GL.GL_TEXTURE25,
		GL.GL_TEXTURE26,
		GL.GL_TEXTURE27,
		GL.GL_TEXTURE28,
		GL.GL_TEXTURE29,
		GL.GL_TEXTURE30,
		GL.GL_TEXTURE31
	};
	
	/** 
	 * Prepare this class for use.  Call this before using other functions.
	 */
	public static void init() {
		if (isSupported()) {
			getMaxTextureUnits();
		}
	}
	
	/**
	 * return true if ARBMultitexture extension is supported by the current OpenGL context.
	 */
	public static boolean isSupported() {
		if (supported == -1) {
			if (extensionExists("GL_ARB_multitexture")) {
				supported = 1;
			}
			else {
				supported = 0;
			}
		}
		return supported==1;
	}
	
	/**
	 * return number of textures that can be layered in the current OpenGL context
	 */
	public static int getMaxTextureUnits() {
		if (isSupported()) {
			if (maxTextureUnits == 0) {
				maxTextureUnits = getSettingInt(GL.GL_MAX_TEXTURE_IMAGE_UNITS);
				if (textures.length != maxTextureUnits) {
					textures = new int[maxTextureUnits];
				}
				msg("GLMultiTexture: Texture Units available = " + maxTextureUnits);
			}
		}
		return maxTextureUnits;
	}
	
	/**
	 * activate a texture in one of the ARBMultitexture slots.  Most graphics cards allow
	 * 4 textures to be layered, though the max allowed by the multitexture extension is 32.
	 * @param layerNumber    which slot to put this texture into (0 - maxTextureUnits()) 
	 * @param textureHandle  handle of a loaded texture
	 */
	public static void activateTexture(int layerNumber, int textureHandle) {
		if (isSupported()) {
			int maxTextures = getMaxTextureUnits();
			if (layerNumber < 0 || layerNumber >= maxTextures) {
				layerNumber = 0;
			}
			// activate texture in the given slot
			gl.glActiveTexture(ARB_TEXTURE_NUMBERS[layerNumber]);
			gl.glBindTexture(GL.GL_TEXTURE_2D, textureHandle);
			gl.glEnable(GL.GL_TEXTURE_2D);
			textures[layerNumber] = textureHandle;
		}
	}
	
	/**
	 * activate a texture in one of the ARBMultitexture slots.  Most graphics cards allow
	 * 4 textures to be layered, though the max allowed by the multitexture extension is 32.
	 * @param layerNumber    which slot to put this texture into (0 - maxTextureUnits()) 
	 * @param textureHandle  handle of a loaded texture
	 */
	public static void disableTexture(int layerNumber) {
		if (isSupported()) {
			int maxTextures = getMaxTextureUnits();
			if (layerNumber < 0 || layerNumber >= maxTextures) {
				layerNumber = 0;
			}
			// disable the given slot
			gl.glActiveTexture(ARB_TEXTURE_NUMBERS[layerNumber]);
			gl.glDisable(GL.GL_TEXTURE_2D);
		}
	}

	/**
	 * draw a multitextured quad (two textures only for now, texture units 0 and 1)
	 * 
	 * @param x  xyz position of quad
	 * @param y
	 * @param z
	 * @param w  size of quad
	 * @param h
	 */
	public static void drawQuadMT(float x, float y, float z, float w, float h) {
		int textureUnitA = GL.GL_TEXTURE0;
		int textureUnitB = GL.GL_TEXTURE1;
		int textureUnitC = GL.GL_TEXTURE2;

		if (isSupported()) {
			// draw  textured quad
			gl.glNormal3f(0.0f, 0.0f, 1.0f); // normal faces positive Z
			gl.glBegin(GL.GL_QUADS); 
			{
				gl.glMultiTexCoord2f(textureUnitA, 0f, 0f);
				gl.glMultiTexCoord2f(textureUnitB, 0f, 0f);
				gl.glMultiTexCoord2f(textureUnitC, 0f, 0f);
				gl.glVertex3f( x, y, z);
				
				gl.glMultiTexCoord2f(textureUnitA, 1f, 0f);
				gl.glMultiTexCoord2f(textureUnitB, 1f, 0f); 
				gl.glMultiTexCoord2f(textureUnitC, 1f, 0f); 
				gl.glVertex3f( x+w, y, z);
				
				gl.glMultiTexCoord2f(textureUnitA, 1f, 1f);
				gl.glMultiTexCoord2f(textureUnitB, 1f, 1f);
				gl.glMultiTexCoord2f(textureUnitC, 1f, 1f);
				gl.glVertex3f( x+w, y+h, z);
				
				gl.glMultiTexCoord2f(textureUnitA, 0f, 1f);
				gl.glMultiTexCoord2f(textureUnitB, 0f, 1f);  
				gl.glMultiTexCoord2f(textureUnitC, 0f, 1f);  
				gl.glVertex3f( x, y+h, z);
			}
			gl.glEnd();
		}
	}
}
